From: Dirk Brenken Date: Thu, 10 Apr 2025 21:43:46 +0000 (+0200) Subject: luci-app-adblock: sync with release 4.4.0-1 X-Git-Url: http://git.openwrt.org/%22https:/collectd.org//%22/%22https:/collectd.org/%22?a=commitdiff_plain;h=2967f2ccd8fd1582415a366462a2a1a224d203d8;p=project%2Fluci.git luci-app-adblock: sync with release 4.4.0-1 * major LuCI frontend improvements, incl. Custom Feed Editor * synchronized with the banIP front-end level Signed-off-by: Dirk Brenken --- diff --git a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/allowlist.js b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/allowlist.js new file mode 100644 index 0000000000..e6374e3f74 --- /dev/null +++ b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/allowlist.js @@ -0,0 +1,58 @@ +'use strict'; +'require view'; +'require fs'; +'require ui'; + +let localFile = '/etc/adblock/adblock.allowlist'; +let notMsg, errMsg; + +return view.extend({ + load: function () { + return L.resolveDefault(fs.stat(localFile), "") + .then(function (stat) { + if (!stat) { + return fs.write(localFile, ""); + } + return Promise.all([ + L.resolveDefault(fs.stat(localFile), ""), + L.resolveDefault(fs.read_direct(localFile), "") + ]); + }); + }, + render: function (allowlist) { + if (allowlist[0] && allowlist[0].size >= 100000) { + document.body.scrollTop = document.documentElement.scrollTop = 0; + ui.addNotification(null, E('p', _('The allowlist is too big, unable to save modifications.')), 'error'); + } + return E('div', { 'class': 'cbi-section cbi-section-descr' }, [ + E('p', _('This is the local adblock allowlist to always-allow certain domains.
\ + Please note: add only one domain per line. Comments introduced with \'#\' are allowed - ip addresses, wildcards and regex are not.')), + E('textarea', { + 'style': 'width: 100% !important; padding: 5px; font-family: monospace; margin-top: .4em', + 'spellcheck': 'false', + 'wrap': 'off', + 'rows': 25 + }, [allowlist[1] != null ? allowlist[1] : '']) + ]); + }, + handleSave: function (ev) { + let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n/g, '\n')) + '\n'; + return fs.write(localFile, value) + .then(function () { + document.querySelector('textarea').value = value; + document.body.scrollTop = document.documentElement.scrollTop = 0; + if (!notMsg) { + ui.addNotification(null, E('p', _('Allowlist modifications have been saved, reload adblock that changes take effect.')), 'info'); + notMsg = true; + } + }).catch(function (e) { + document.body.scrollTop = document.documentElement.scrollTop = 0; + if (!errMsg) { + ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error'); + errMsg = true; + } + }); + }, + handleSaveApply: null, + handleReset: null +}); diff --git a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/blacklist.js b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/blacklist.js deleted file mode 100644 index 9f5c6c4b06..0000000000 --- a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/blacklist.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; -'require view'; -'require fs'; -'require ui'; - -return view.extend({ - load: function() { - return L.resolveDefault(fs.read_direct('/etc/adblock/adblock.blacklist'), ''); - }, - handleSave: function(ev) { - var value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n/g, '\n')) + '\n'; - return fs.write('/etc/adblock/adblock.blacklist', value) - .then(function(rc) { - document.querySelector('textarea').value = value; - ui.addNotification(null, E('p', _('The changes to the blacklist have been saved. Reload your adblock lists for the changes to take effect.')), 'info'); - }).catch(function(e) { - ui.addNotification(null, E('p', _('Unable to save changes: %s').format(e.message))); - }); - }, - render: function(blacklist) { - return E([ - E('p', {}, - _('This is the local adblock blacklist to always-deny certain (sub) domains.
\ - Please note: add only one domain per line. Comments introduced with \'#\' are allowed - ip addresses, wildcards and regex are not.')), - E('p', {}, - E('textarea', { - 'style': 'width: 100% !important; padding: 5px; font-family: monospace', - 'spellcheck': 'false', - 'wrap': 'off', - 'rows': 25 - }, [ blacklist != null ? blacklist : '' ]) - ) - ]); - }, - handleSaveApply: null, - handleReset: null -}); diff --git a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/blocklist.js b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/blocklist.js new file mode 100644 index 0000000000..9fec0895af --- /dev/null +++ b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/blocklist.js @@ -0,0 +1,58 @@ +'use strict'; +'require view'; +'require fs'; +'require ui'; + +let localFile = '/etc/adblock/adblock.blocklist'; +let notMsg, errMsg; + +return view.extend({ + load: function () { + return L.resolveDefault(fs.stat(localFile), "") + .then(function (stat) { + if (!stat) { + return fs.write(localFile, ""); + } + return Promise.all([ + L.resolveDefault(fs.stat(localFile), ""), + L.resolveDefault(fs.read_direct(localFile), "") + ]); + }); + }, + render: function (blocklist) { + if (blocklist[0] && blocklist[0].size >= 100000) { + document.body.scrollTop = document.documentElement.scrollTop = 0; + ui.addNotification(null, E('p', _('The blocklist is too big, unable to save modifications.')), 'error'); + } + return E('div', { 'class': 'cbi-section cbi-section-descr' }, [ + E('p', _('This is the local adblock blocklist to always-block certain domains.
\ + Please note: add only one domain per line. Comments introduced with \'#\' are allowed - ip addresses, wildcards and regex are not.')), + E('textarea', { + 'style': 'width: 100% !important; padding: 5px; font-family: monospace; margin-top: .4em', + 'spellcheck': 'false', + 'wrap': 'off', + 'rows': 25 + }, [blocklist[1] != null ? blocklist[1] : '']) + ]); + }, + handleSave: function (ev) { + let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n/g, '\n')) + '\n'; + return fs.write(localFile, value) + .then(function () { + document.querySelector('textarea').value = value; + document.body.scrollTop = document.documentElement.scrollTop = 0; + if (!notMsg) { + ui.addNotification(null, E('p', _('Blocklist modifications have been saved, reload adblock that changes take effect.')), 'info'); + notMsg = true; + } + }).catch(function (e) { + document.body.scrollTop = document.documentElement.scrollTop = 0; + if (!errMsg) { + ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error'); + errMsg = true; + } + }); + }, + handleSaveApply: null, + handleReset: null +}); diff --git a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/custom.css b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/custom.css new file mode 100644 index 0000000000..0209f7d092 --- /dev/null +++ b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/custom.css @@ -0,0 +1,9 @@ +.cbi-input-text { + width: 90% !important; + margin-bottom: -5px; + padding-top: 0rem; +} +.cbi-input-select { + margin-bottom: -5px; + padding-top: 0rem; +} diff --git a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/dnsreport.js b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/dnsreport.js index 010e728cf1..e8806db3e9 100644 --- a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/dnsreport.js +++ b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/dnsreport.js @@ -7,12 +7,12 @@ button handling */ function handleAction(ev) { - if (ev.target && ev.target.getAttribute('name') === 'blacklist') { - L.ui.showModal(_('Add Blacklist Domain'), [ - E('p', _('Add this (sub-)domain to your local blacklist.')), + if (ev.target && ev.target.getAttribute('name') === 'blocklist') { + L.ui.showModal(_('Add Blocklist Domain'), [ + E('p', _('Add this (sub-)domain to your local blocklist.')), E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [ E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [ - E('input', { 'class': 'cbi-input-text', 'style': 'width:300px', 'spellcheck': 'false', 'id': 'blacklist', 'value': ev.target.getAttribute('value') }, []) + E('input', { 'class': 'cbi-input-text', 'style': 'width:300px', 'spellcheck': 'false', 'id': 'blocklist', 'value': ev.target.getAttribute('value') }, []) ]) ]), E('div', { 'class': 'right' }, [ @@ -24,14 +24,14 @@ function handleAction(ev) { E('button', { 'class': 'btn cbi-button-action', 'click': ui.createHandlerFn(this, function(ev) { - L.resolveDefault(fs.read_direct('/etc/adblock/adblock.blacklist'), '') + L.resolveDefault(fs.read_direct('/etc/adblock/adblock.blocklist'), '') .then(function(res) { - var domain = document.getElementById('blacklist').value.trim().toLowerCase().replace(/[^a-z0-9\.\-]/g,''); + var domain = document.getElementById('blocklist').value.trim().toLowerCase().replace(/[^a-z0-9\.\-]/g,''); var pattern = new RegExp('^' + domain.replace(/[\.]/g,'\\.') + '$', 'm'); if (res.search(pattern) === -1) { - var blacklist = res + domain + '\n'; - fs.write('/etc/adblock/adblock.blacklist', blacklist); - ui.addNotification(null, E('p', _('Blacklist changes have been saved. Refresh your adblock lists that changes take effect.')), 'info'); + var blocklist = res + domain + '\n'; + fs.write('/etc/adblock/adblock.blocklist', blocklist); + ui.addNotification(null, E('p', _('Blocklist modifications have been saved, reload adblock that changes take effect.')), 'info'); } L.hideModal(); }); @@ -39,15 +39,15 @@ function handleAction(ev) { }, _('Save')) ]) ]); - document.getElementById('blacklist').focus(); + document.getElementById('blocklist').focus(); } - if (ev.target && ev.target.getAttribute('name') === 'whitelist') { - L.ui.showModal(_('Add Whitelist Domain'), [ - E('p', _('Add this (sub-)domain to your local whitelist.')), + if (ev.target && ev.target.getAttribute('name') === 'allowlist') { + L.ui.showModal(_('Add Allowlist Domain'), [ + E('p', _('Add this (sub-)domain to your local allowlist.')), E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [ E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [ - E('input', { 'class': 'cbi-input-text', 'style': 'width:300px', 'spellcheck': 'false', 'id': 'whitelist', 'value': ev.target.getAttribute('value') }, []) + E('input', { 'class': 'cbi-input-text', 'style': 'width:300px', 'spellcheck': 'false', 'id': 'allowlist', 'value': ev.target.getAttribute('value') }, []) ]) ]), E('div', { 'class': 'right' }, [ @@ -59,14 +59,14 @@ function handleAction(ev) { E('button', { 'class': 'btn cbi-button-action', 'click': ui.createHandlerFn(this, function(ev) { - L.resolveDefault(fs.read_direct('/etc/adblock/adblock.whitelist'), '') + L.resolveDefault(fs.read_direct('/etc/adblock/adblock.allowlist'), '') .then(function(res) { - var domain = document.getElementById('whitelist').value.trim().toLowerCase().replace(/[^a-z0-9\.\-]/g,''); + var domain = document.getElementById('allowlist').value.trim().toLowerCase().replace(/[^a-z0-9\.\-]/g,''); var pattern = new RegExp('^' + domain.replace(/[\.]/g,'\\.') + '$', 'm'); if (res.search(pattern) === -1) { - var whitelist = res + domain + '\n'; - fs.write('/etc/adblock/adblock.whitelist', whitelist); - ui.addNotification(null, E('p', _('Whitelist changes have been saved. Refresh your adblock lists that changes take effect.')), 'info'); + var allowlist = res + domain + '\n'; + fs.write('/etc/adblock/adblock.allowlist', allowlist); + ui.addNotification(null, E('p', _('Allowlist modifications have been saved, reload adblock that changes take effect.')), 'info'); } L.hideModal(); }); @@ -74,7 +74,7 @@ function handleAction(ev) { }, _('Save')) ]) ]); - document.getElementById('whitelist').focus(); + document.getElementById('allowlist').focus(); } if (ev === 'query') { @@ -282,18 +282,18 @@ return view.extend({ button = E('button', { 'class': 'btn cbi-button cbi-button-positive', 'style': 'word-break: inherit', - 'name': 'whitelist', + 'name': 'allowlist', 'value': content.requests[i].domain, 'click': handleAction - }, [ _('Whitelist...') ]); + }, [ _('Allowlist...') ]); } else { button = E('button', { 'class': 'btn cbi-button cbi-button-negative', 'style': 'word-break: inherit', - 'name': 'blacklist', + 'name': 'blocklist', 'value': content.requests[i].domain, 'click': handleAction - }, [ _('Blacklist...') ]); + }, [ _('Blocklist...') ]); } rows_requests.push([ content.requests[i].date, @@ -326,22 +326,7 @@ return view.extend({ E('div', { 'class': 'cbi-value' }, [ E('div', { 'class': 'cbi-value-title', 'style': 'float:left;width:230px' }, _('Blocked DNS Requests')), E('div', { 'class': 'cbi-value-title', 'id': 'blocked', 'style': 'float:left;color:#37c' }, (content.blocked || '-') + ' (' + (content.percent || '-') + ')') - ]), - E('div', { 'class': 'right' }, [ - E('button', { - 'class': 'btn cbi-button cbi-button-apply', - 'click': ui.createHandlerFn(this, function() { - return handleAction('query'); - }) - }, [ _('Blocklist Query...') ]), - '\xa0\xa0\xa0', - E('button', { - 'class': 'btn cbi-button cbi-button-positive', - 'click': ui.createHandlerFn(this, function() { - return handleAction('refresh'); - }) - }, [ _('Refresh...') ]) - ]), + ]) ]), E('div', { 'class': 'cbi-section' }, [ E('div', { 'class': 'left' }, [ @@ -355,7 +340,24 @@ return view.extend({ E('h3', _('Latest DNS Requests')), tbl_requests ]) - ]) + ]), + E('div', { 'class': 'cbi-page-actions' }, [ + E('button', { + 'class': 'btn cbi-button cbi-button-apply', + 'style': 'float:none;margin-right:.4em;', + 'click': ui.createHandlerFn(this, function() { + return handleAction('query'); + }) + }, [ _('Blocklist Query...') ]), + E('button', { + 'class': 'btn cbi-button cbi-button-positive important', + 'style': 'float:none;margin-right:.4em;', + 'click': ui.createHandlerFn(this, function() { + return handleAction('refresh'); + }) + }, [ _('Refresh...') ]) + ]), + ]); }, handleSaveApply: null, diff --git a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/feeds.js b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/feeds.js new file mode 100644 index 0000000000..acfa8995d2 --- /dev/null +++ b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/feeds.js @@ -0,0 +1,292 @@ +'use strict'; +'require view'; +'require form'; +'require fs'; +'require ui'; + +/* + include custom CSS +*/ +document.querySelector('head').appendChild(E('link', { + 'rel': 'stylesheet', + 'type': 'text/css', + 'href': L.resource('view/adblock/custom.css') +})); + +/* + observe DOM changes +*/ +const observer = new MutationObserver(function (mutations) { + if (mutations) { + const inputs = document.querySelectorAll('input'); + inputs.forEach(function (input) { + input.setAttribute('autocomplete', 'off') + input.setAttribute('autocorrect', 'off') + input.setAttribute('autocapitalize', 'off') + input.setAttribute('spellcheck', false) + }) + const labels = document.querySelectorAll('label[for^="widget.cbid.json"][for$="name"]'); + labels.forEach(function (label) { + label.setAttribute("style", "font-weight: bold !important; color: #595 !important;"); + }) + L.resolveDefault(fs.stat('/etc/adblock/adblock.custom.feeds'), '').then(function (stat) { + const buttons = document.querySelectorAll('#btnClear, #btnCreate, #btnSave, #btnUpload, #btnDownload'); + if (buttons[1] && buttons[2] && stat.size === 0) { + buttons[1].removeAttribute('disabled'); + buttons[2].removeAttribute('disabled'); + } else if (buttons[0] && buttons[3] && buttons[4] && stat.size > 0) { + buttons[0].removeAttribute('disabled'); + buttons[3].removeAttribute('disabled'); + buttons[4].removeAttribute('disabled'); + } + }); + } +}); + +const targetNode = document.getElementById('view'); +const observerConfig = { + childList: true, + subtree: true, + attributes: false, + characterData: false +}; +observer.observe(targetNode, observerConfig); + +/* + button handling +*/ +function handleEdit(ev) { + if (ev === 'upload') { + return ui.uploadFile('/etc/adblock/adblock.custom.feeds').then(function () { + L.resolveDefault(fs.read_direct('/etc/adblock/adblock.custom.feeds', 'json'), "").then(function (data) { + if (data) { + let dataLength = Object.keys(data).length || 0; + if (dataLength > 0) { + for (let i = 0; i < dataLength; i++) { + let feed = Object.keys(data)[i]; + let descr = data[feed].descr; + if (feed && descr) { + continue; + } + fs.write('/etc/adblock/adblock.custom.feeds', null).then(function () { + return ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error'); + }); + } + } else { + fs.write('/etc/adblock/adblock.custom.feeds', null).then(function () { + return ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error'); + }); + } + location.reload(); + } else { + fs.write('/etc/adblock/adblock.custom.feeds', null).then(function () { + return ui.addNotification(null, E('p', _('Upload of the custom feed file failed.')), 'error'); + }); + } + }); + }).catch(function () { }); + } + if (ev === 'download') { + return fs.read_direct('/etc/adblock/adblock.custom.feeds', 'blob').then(function (blob) { + let url = window.URL.createObjectURL(blob), + date = new Date(), + name = 'adblock.custom.feeds_%04d-%02d-%02d.json'.format(date.getFullYear(), date.getMonth() + 1, date.getDate()), + link = E('a', { 'style': 'display:none', 'href': url, 'download': name }); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + }).catch(function () { }); + } + if (ev === 'create') { + return fs.read_direct('/etc/adblock/adblock.feeds', 'json').then(function (content) { + fs.write('/etc/adblock/adblock.custom.feeds', JSON.stringify(content)).then(function () { + location.reload(); + }); + }); + } + if (ev === 'clear') { + return fs.write('/etc/adblock/adblock.custom.feeds', null).then(function () { + location.reload(); + }); + } + if (ev === 'save') { + const invalid = document.querySelectorAll('.cbi-input-invalid'); + if (invalid.length > 0) { + document.body.scrollTop = document.documentElement.scrollTop = 0; + return ui.addNotification(null, E('p', _('Invalid input values, unable to save modifications.')), 'error'); + } + } + let sumSubElements = [], exportJson; + const nodeKeys = document.querySelectorAll('[id^="widget.cbid.json"][id$="name"]'); + for (let i = 0; i < nodeKeys.length; i++) { + let subElements = {}; + let elements = document.querySelectorAll('[id^="widget.cbid.json.' + nodeKeys[i].id.split('.')[3] + '\."]'); + for (const element of elements) { + let key = element.id.split('.')[4]; + let value = element.value || ""; + if (value === "") { + continue; + } + switch (key) { + case 'url': + subElements.url = value; + break; + case 'rule': + subElements.rule = value; + break; + case 'size': + subElements.size = value; + break; + case 'descr': + subElements.descr = value; + break; + } + } + if (nodeKeys[i].value !== "" && subElements.descr !== "") { + sumSubElements.push(nodeKeys[i].value, subElements); + } + } + if (sumSubElements.length > 0) { + exportJson = JSON.stringify(sumSubElements).replace(/^\[/, '{\n').replace(/\}]$/, '\n\t}\n}\n').replace(/,{"/g, ':{\n\t"').replace(/"},"/g, '"\n\t},\n"').replace(/","/g, '",\n\t"'); + } + return fs.write('/etc/adblock/adblock.custom.feeds', exportJson).then(function () { + location.reload(); + }); +} + +return view.extend({ + load: function () { + return L.resolveDefault(fs.stat('/etc/adblock/adblock.custom.feeds'), "") + .then(function (stat) { + if (!stat) { + return fs.write('/etc/adblock/adblock.custom.feeds', ""); + } + return L.resolveDefault(fs.read_direct('/etc/adblock/adblock.custom.feeds', 'json'), ""); + }); + }, + + render: function (data) { + let m, s, o, feed, url, rule, size, descr; + + m = new form.JSONMap(data, null, _('With this editor you can upload your local custom feed file or fill up an initial one (a 1:1 copy of the version shipped with the package). \ + The file is located at \'/etc/adblock/adblock.custom.feeds\'. \ + Then you can edit this file, delete entries, add new ones or make a local backup. To go back to the maintainers version just clear the custom feed file.')); + for (let i = 0; i < Object.keys(m.data.data).length; i++) { + feed = Object.keys(m.data.data)[i]; + url = m.data.data[feed].url; + rule = m.data.data[feed].rule; + size = m.data.data[feed].size; + descr = m.data.data[feed].descr; + + s = m.section(form.TypedSection, feed, null); + s.addremove = true; + s.anonymous = true; + + o = s.option(form.Value, 'name', _('Feed Name')); + o.ucioption = '.name'; + o.datatype = 'and(minlength(3),maxlength(20))'; + o.validate = function (section_id, value) { + if (!value) { + return _('Empty field not allowed'); + } + if (!value.match(/^[a-z0-9_]+$/)) { + return _('Invalid characters'); + } + return true; + } + + o = s.option(form.Value, 'url', _('URL')); + o.validate = function (section_id, value) { + if (!value) { + return true; + } + if (!value.match(/^(http:\/\/|https:\/\/)[A-Za-z0-9\/\.\-\?\&\+_@%=:~#]+$/)) { + return _('Protocol/URL format not supported'); + } + return true; + } + + o = s.option(form.ListValue, 'rule', _('Rule')); + o.value('/^([[:alnum:]_-]{1,63}\\.)+[[:alpha:]]+([[:space:]]|$)/{print tolower($1)}', _('')); + o.value('/^127\\.0\\.0\\.1[[:space:]]+([[:alnum:]_-]{1,63}\\.)+[[:alpha:]]+([[:space:]]|$)/{print tolower($2)}', _('127.0.0.1')); + o.value('/^0\\.0\\.0\\.0[[:space:]]+([[:alnum:]_-]{1,63}\\.)+[[:alpha:]]+([[:space:]]|$)/{print tolower($2)}', _('0.0.0.0')); + o.value('BEGIN{FS=\"[|^]\"}/^\\|\\|([[:alnum:]_-]{1,63}\\.)+[[:alpha:]]+\\^(\\$third-party)?$/{print tolower($3)}', _('')); + o.value('BEGIN{FS=\"\/\"}/^http[s]?:\\/\\/([[:alnum:]_-]{1,63}\\.)+[[:alpha:]]+(\\/|$)/{print tolower($3)}', _('')); + o.optional = true; + o.rmempty = true; + + o = s.option(form.ListValue, 'size', _('Size')); + o.value('S', _('Small')); + o.value('M', _('Medium')); + o.value('L', _('Large')); + o.value('XL', _('Extra Large')); + o.value('XXL', _('Extra Extra Large')); + o.value('VAR', _('Varying')); + + o = s.option(form.Value, 'descr', _('Description')); + o.datatype = 'and(minlength(3),maxlength(30))'; + o.validate = function (section_id, value) { + if (!value) { + return _('Empty field not allowed'); + } + return true; + } + } + + s = m.section(form.NamedSection, 'global'); + s.render = L.bind(function () { + return E('div', { 'class': 'cbi-page-actions' }, [ + E('button', { + 'class': 'btn cbi-button cbi-button-action important', + 'style': 'float:none;margin-right:.4em;', + 'id': 'btnDownload', + 'disabled': 'disabled', + 'click': ui.createHandlerFn(this, function () { + return handleEdit('download'); + }) + }, [_('Download')]), + E('button', { + 'class': 'btn cbi-button cbi-button-action important', + 'style': 'float:none;margin-right:.4em;', + 'id': 'btnUpload', + 'disabled': 'disabled', + 'click': ui.createHandlerFn(this, function () { + return handleEdit('upload'); + }) + }, [_('Upload')]), + E('button', { + 'class': 'btn cbi-button cbi-button-action important', + 'style': 'float:none;margin-right:.4em;', + 'id': 'btnCreate', + 'disabled': 'disabled', + 'click': ui.createHandlerFn(this, function () { + return handleEdit('create'); + }) + }, [_('Fill')]), + E('button', { + 'class': 'btn cbi-button cbi-button-negative important', + 'style': 'float:none;margin-right:.4em;', + 'id': 'btnClear', + 'disabled': 'disabled', + 'click': ui.createHandlerFn(this, function () { + return handleEdit('clear'); + }) + }, [_('Clear')]), + E('button', { + 'class': 'btn cbi-button cbi-button-positive important', + 'style': 'float:none', + 'id': 'btnSave', + 'disabled': 'disabled', + 'click': ui.createHandlerFn(this, function () { + return handleEdit('save'); + }) + }, [_('Save')]), + ]) + }); + return m.render(); + }, + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/overview.js b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/overview.js index 9f0de8beb4..a17a56f92c 100644 --- a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/overview.js +++ b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/overview.js @@ -1,4 +1,5 @@ 'use strict'; +'require dom'; 'require view'; 'require poll'; 'require fs'; @@ -11,126 +12,34 @@ button handling */ function handleAction(ev) { - if (ev === 'timer') { - L.ui.showModal(_('Refresh Timer'), [ - E('p', _('To keep your adblock lists up-to-date, you should set up an automatic update job for these lists.')), - E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [ - E('h5', _('Existing job(s)')), - E('textarea', { - 'id': 'cronView', - 'style': 'width: 100% !important; padding: 5px; font-family: monospace', - 'readonly': 'readonly', - 'wrap': 'off', - 'rows': 5 - }) - ]), - E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [ - E('label', { 'class': 'cbi-input-select', 'style': 'padding-top:.5em' }, [ - E('h5', _('Set a new adblock job')), - E('select', { 'class': 'cbi-input-select', 'id': 'timerA' }, [ - E('option', { 'value': 'start' }, 'Start'), - E('option', { 'value': 'reload' }, 'Reload'), - E('option', { 'value': 'restart' }, 'Restart'), - E('option', { 'value': 'suspend' }, 'Suspend'), - E('option', { 'value': 'resume' }, 'Resume'), - E('option', { 'value': 'report gen' }, 'Report'), - E('option', { 'value': 'report mail' }, 'Report & Mail') - ]), - '\xa0\xa0\xa0', - _('Adblock action') - ]), - E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [ - E('input', { 'class': 'cbi-input-text', 'id': 'timerH', 'maxlength': '2' }, [ - ]), - '\xa0\xa0\xa0', - _('The hours portition (req., range: 0-23)') - ]), - E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [ - E('input', { 'class': 'cbi-input-text', 'id': 'timerM', 'maxlength': '2' }), - '\xa0\xa0\xa0', - _('The minutes portion (opt., range: 0-59)') - ]), - E('label', { 'class': 'cbi-input-text', 'style': 'padding-top:.5em' }, [ - E('input', { 'class': 'cbi-input-text', 'id': 'timerD', 'maxlength': '13' }), - '\xa0\xa0\xa0', - _('The day of the week (opt., values: 0-6 possibly sep. by , or -)') - ]) - ]), - E('div', { 'class': 'left', 'style': 'display:flex; flex-direction:column' }, [ - E('label', { 'class': 'cbi-input-select', 'style': 'padding-top:.5em' }, [ - E('h5', _('Remove an existing job')), - E('input', { 'class': 'cbi-input-text', 'id': 'lineno', 'maxlength': '2' }, [ - ]), - '\xa0\xa0\xa0', - _('Line number to remove') - ]) - ]), - E('div', { 'class': 'right' }, [ - E('button', { - 'class': 'btn cbi-button', - 'click': L.hideModal - }, _('Cancel')), - ' ', - E('button', { - 'class': 'btn cbi-button-action', - 'click': ui.createHandlerFn(this, function(ev) { - var lineno = document.getElementById('lineno').value; - var action = document.getElementById('timerA').value; - var hours = document.getElementById('timerH').value; - var minutes = document.getElementById('timerM').value || '0'; - var days = document.getElementById('timerD').value || '*'; - if (hours) { - L.resolveDefault(fs.exec_direct('/etc/init.d/adblock', ['timer', 'add', action, hours, minutes, days])) - .then(function(res) { - if (res) { - ui.addNotification(null, E('p', _('The Refresh Timer could not been updated.')), 'error'); - } else { - ui.addNotification(null, E('p', _('The Refresh Timer has been updated.')), 'info'); - } - }); - } else if (lineno) { - L.resolveDefault(fs.exec_direct('/etc/init.d/adblock', ['timer', 'remove', lineno])) - .then(function(res) { - if (res) { - ui.addNotification(null, E('p', _('The Refresh Timer could not been updated.')), 'error'); - } else { - ui.addNotification(null, E('p', _('The Refresh Timer has been updated.')), 'info'); - } - }); - } else { - document.getElementById('timerH').focus(); - return - } - L.hideModal(); - }) - }, _('Save')) - ]) - ]); - L.resolveDefault(fs.exec_direct('/etc/init.d/adblock', ['timer', 'list'])) - .then(function(res) { - document.getElementById('cronView').value = res.trim(); - }); - document.getElementById('timerH').focus(); - return - } - - if (document.getElementById('status') && document.getElementById('status').textContent.substr(0,6) === 'paused') { + if (ev !== 'stop' && + document.getElementById('status') && + document.getElementById('status').textContent.substring(0, 6) === 'paused') { ev = 'resume'; } - - fs.exec_direct('/etc/init.d/adblock', [ev]) + if (ev === 'restart' || ev === 'reload') { + let map = document.querySelector('.cbi-map'); + dom.callClassMethod(map, 'save') + .then(L.bind(ui.changes.apply, ui.changes)) + .then(function () { + return fs.exec_direct('/etc/init.d/adblock', [ev]); + }) + } else { + return fs.exec_direct('/etc/init.d/adblock', [ev]); + } } return view.extend({ - load: function() { + load: function () { return Promise.all([ - L.resolveDefault(fs.exec_direct('/etc/init.d/adblock', ['list']), {}), + L.resolveDefault(fs.read_direct('/etc/adblock/adblock.custom.feeds'), ''), + L.resolveDefault(fs.read_direct('/etc/adblock/adblock.feeds'), ''), L.resolveDefault(fs.read_direct('/etc/adblock/adblock.categories'), ''), uci.load('adblock') ]); }, - render: function(result) { + render: function (result) { let m, s, o; m = new form.Map('adblock', 'Adblock', _('Configuration of the adblock package to block ad/abuse domains by using DNS. \ @@ -139,10 +48,19 @@ return view.extend({ /* poll runtime information */ - pollData: poll.add(function() { - return L.resolveDefault(fs.read_direct('/tmp/adb_runtime.json'), 'null').then(function(res) { - var info = JSON.parse(res); + pollData: poll.add(function () { + return L.resolveDefault(fs.read_direct('/var/run/adb_runtime.json'), 'null').then(function (res) { var status = document.getElementById('status'); + try { + var info = JSON.parse(res); + } catch (e) { + status.textContent = '-'; + poll.stop(); + if (status.classList.contains('spinning')) { + status.classList.remove('spinning'); + } + ui.addNotification(null, E('p', _('Unable to parse the runtime information!')), 'error'); + } if (status && info) { status.textContent = (info.adblock_status || '-') + ' / ' + (info.adblock_version || '-'); if (info.adblock_status === "running") { @@ -153,39 +71,40 @@ return view.extend({ if (status.classList.contains("spinning")) { status.classList.remove("spinning"); if (document.getElementById('btn_suspend')) { - if (status.textContent.substr(0,6) === 'paused') { + if (status.textContent.substring(0, 6) === 'paused') { document.querySelector('#btn_suspend').textContent = 'Resume'; } - if (document.getElementById('status').textContent.substr(0,7) === 'enabled') { + if (document.getElementById('status').textContent.substring(0, 7) === 'enabled') { document.querySelector('#btn_suspend').textContent = 'Suspend'; } } } } - if (status.textContent.substr(0,6) === 'paused' && document.getElementById('btn_suspend')) { + if (status.textContent.substring(0, 6) === 'paused' && document.getElementById('btn_suspend')) { document.querySelector('#btn_suspend').textContent = 'Resume'; } } else if (status) { status.textContent = '-'; - if (status.classList.contains("spinning")) { - status.classList.remove("spinning"); + poll.stop(); + if (status.classList.contains('spinning')) { + status.classList.remove('spinning'); } } var domains = document.getElementById('domains'); if (domains && info) { domains.textContent = info.blocked_domains || '-'; } - var sources = document.getElementById('sources'); + var feeds = document.getElementById('feeds'); var src_array = []; - if (sources && info) { - for (var i = 0; i < info.active_sources.length; i++) { - if (i < info.active_sources.length-1) { - src_array += info.active_sources[i] + ', '; + if (feeds && info) { + for (var i = 0; i < info.active_feeds.length; i++) { + if (i < info.active_feeds.length - 1) { + src_array += info.active_feeds[i] + ', '; } else { - src_array += info.active_sources[i] + src_array += info.active_feeds[i] } } - sources.textContent = src_array || '-'; + feeds.textContent = src_array || '-'; } var backend = document.getElementById('backend'); if (backend && info) { @@ -211,81 +130,59 @@ return view.extend({ if (run && info) { run.textContent = info.last_run || '-'; } + var sys = document.getElementById('sys'); + if (sys && info) { + sys.textContent = info.system_info || '-'; + } }); - }, 1); + }, 2); /* runtime information and buttons */ s = m.section(form.NamedSection, 'global'); - s.render = L.bind(function(view, section_id) { + s.render = L.bind(function (view, section_id) { return E('div', { 'class': 'cbi-section' }, [ - E('h3', _('Information')), + E('h3', _('Information')), E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Status / Version')), - E('div', { 'class': 'cbi-value-field spinning', 'id': 'status', 'style': 'color:#37c' },'\xa0') + E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Status / Version')), + E('div', { 'class': 'cbi-value-field spinning', 'id': 'status', 'style': 'margin-bottom:-5px;color:#37c;' }, '\xa0') ]), E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Blocked Domains')), - E('div', { 'class': 'cbi-value-field', 'id': 'domains', 'style': 'color:#37c' },'-') + E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Blocked Domains')), + E('div', { 'class': 'cbi-value-field', 'id': 'domains', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') ]), E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Active Sources')), - E('div', { 'class': 'cbi-value-field', 'id': 'sources', 'style': 'color:#37c' },'-') + E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Active Feeds')), + E('div', { 'class': 'cbi-value-field', 'id': 'feeds', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') ]), E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('DNS Backend')), - E('div', { 'class': 'cbi-value-field', 'id': 'backend', 'style': 'color:#37c' },'-') + E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('DNS Backend')), + E('div', { 'class': 'cbi-value-field', 'id': 'backend', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') ]), E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Run Utils')), - E('div', { 'class': 'cbi-value-field', 'id': 'utils', 'style': 'color:#37c' },'-') + E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Run Utils')), + E('div', { 'class': 'cbi-value-field', 'id': 'utils', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') ]), E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Run Interfaces')), - E('div', { 'class': 'cbi-value-field', 'id': 'ifaces', 'style': 'color:#37c' },'-') + E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Run Interfaces')), + E('div', { 'class': 'cbi-value-field', 'id': 'ifaces', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') ]), E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Run Directories')), - E('div', { 'class': 'cbi-value-field', 'id': 'dirs', 'style': 'color:#37c' },'-') + E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Run Directories')), + E('div', { 'class': 'cbi-value-field', 'id': 'dirs', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') ]), E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Run Flags')), - E('div', { 'class': 'cbi-value-field', 'id': 'flags', 'style': 'color:#37c' },'-') + E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Run Flags')), + E('div', { 'class': 'cbi-value-field', 'id': 'flags', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') ]), E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title', 'style': 'padding-top:0rem' }, _('Last Run')), - E('div', { 'class': 'cbi-value-field', 'id': 'run', 'style': 'color:#37c' },'-') + E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Last Run')), + E('div', { 'class': 'cbi-value-field', 'id': 'run', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') ]), - E('div', { class: 'right' }, [ - E('button', { - 'class': 'btn cbi-button cbi-button-apply', - 'click': ui.createHandlerFn(this, function() { - return handleAction('timer'); - }) - }, [ _('Refresh Timer...') ]), - '\xa0\xa0\xa0', - E('button', { - 'class': 'btn cbi-button cbi-button-apply', - 'id': 'btn_suspend', - 'click': ui.createHandlerFn(this, function() { - return handleAction('suspend'); - }) - }, [ _('Suspend') ]), - '\xa0\xa0\xa0', - E('button', { - 'class': 'btn cbi-button cbi-button-positive', - 'click': ui.createHandlerFn(this, function() { - return handleAction('reload'); - }) - }, [ _('Reload') ]), - '\xa0\xa0\xa0', - E('button', { - 'class': 'btn cbi-button cbi-button-negative', - 'click': ui.createHandlerFn(this, function() { - return handleAction('restart'); - }) - }, [ _('Restart') ]) + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('System Info')), + E('div', { 'class': 'cbi-value-field', 'id': 'sys', 'style': 'margin-bottom:-5px;color:#37c;' }, '-') ]) ]); }, o, this); @@ -296,16 +193,21 @@ return view.extend({ */ s = m.section(form.NamedSection, 'global', 'adblock', _('Settings')); s.addremove = false; - s.tab('general', _('General Settings')); + s.tab('general', _('General Settings')); s.tab('additional', _('Additional Settings')); s.tab('adv_dns', _('Advanced DNS Settings')); s.tab('adv_report', _('Advanced Report Settings')); s.tab('adv_email', _('Advanced E-Mail Settings')); - s.tab('sources', _('Blocklist Sources')); + s.tab('feeds', _('Feed Selection')); /* general settings tab */ + o = s.taboption('general', form.DummyValue, '_sub'); + o.rawhtml = true; + o.default = '' + _('Changes on this tab needs an adblock service restart to take effect.') + '' + + '
'; + o = s.taboption('general', form.Flag, 'adb_enabled', _('Enabled'), _('Enable the adblock service.')); o.rmempty = false; @@ -314,17 +216,17 @@ return view.extend({ o.nocreate = true; o.rmempty = true; - o = s.taboption('general', form.Flag, 'adb_forcedns', _('Force Local DNS'), _('Redirect all DNS queries from specified zones to the local DNS resolver, applies to UDP and TCP protocol.')); + o = s.taboption('general', form.Flag, 'adb_dnsforce', _('Force Local DNS'), _('Redirect all DNS queries from specified zones to the local DNS resolver, applies to UDP and TCP protocol.')); o.rmempty = false; o = s.taboption('general', widgets.ZoneSelect, 'adb_zonelist', _('Forced Zones'), _('Firewall source zones that should be forced locally.')); - o.depends('adb_forcedns', '1'); + o.depends('adb_dnsforce', '1'); o.multiple = true; o.nocreate = true; o.rmempty = true; o = s.taboption('general', form.DynamicList, 'adb_portlist', _('Forced Ports'), _('Firewall ports that should be forced locally.')); - o.depends('adb_forcedns', '1'); + o.depends('adb_dnsforce', '1'); o.multiple = true; o.nocreate = false; o.datatype = 'port'; @@ -337,29 +239,26 @@ return view.extend({ o.default = 1 o.rmempty = true; - o = s.taboption('general', form.Flag, 'adb_safesearch', _('Enable SafeSearch'), _('Enforcing SafeSearch for google, bing, duckduckgo, yandex, youtube and pixabay.')); + o = s.taboption('general', form.Flag, 'adb_safesearch', _('Enable SafeSearch'), _('Enforcing SafeSearch for google, bing, brave, duckduckgo, yandex, youtube and pixabay.')); o.rmempty = false; o = s.taboption('general', form.MultiValue, 'adb_safesearchlist', _('Limit SafeSearch'), _('Limit SafeSearch to certain providers.')); o.depends('adb_safesearch', '1'); o.value('google'); o.value('bing'); + o.value('brave'); o.value('duckduckgo'); o.value('yandex'); o.value('youtube'); o.value('pixabay'); o.rmempty = true; - o = s.taboption('general', form.Flag, 'adb_safesearchmod', _('Relax SafeSearch'), _('Enable moderate SafeSearch filters for youtube.')); - o.depends('adb_safesearch', '1'); - o.rmempty = true; - o = s.taboption('general', form.Flag, 'adb_report', _('DNS Report'), _('Gather DNS related network traffic via tcpdump and provide a DNS Report on demand. \ - Please note: this needs additional \'tcpdump\' or \'tcpdump-mini\' package installation and a full adblock service restart to take effect.')); + This needs the additional \'tcpdump\' or \'tcpdump-mini\' package installation and a full adblock service restart to take effect.')); o.rmempty = false; o = s.taboption('general', form.Flag, 'adb_mail', _('E-Mail Notification'), _('Send adblock related notification e-mails. \ - Please note: this needs additional \'msmtp\' package installation.')); + This needs the additional \'msmtp\' package installation.')); o.rmempty = false; o = s.taboption('general', form.Value, 'adb_mailreceiver', _('E-Mail Receiver Address'), _('Receiver address for adblock notification e-mails.')); @@ -370,11 +269,15 @@ return view.extend({ /* additional settings tab */ + o = s.taboption('additional', form.DummyValue, '_sub'); + o.rawhtml = true; + o.default = '' + _('Changes on this tab needs an adblock service restart to take effect.') + '' + + '
'; + o = s.taboption('additional', form.Flag, 'adb_debug', _('Verbose Debug Logging'), _('Enable verbose debug logging in case of any processing errors.')); o.rmempty = false; - o = s.taboption('additional', form.Flag, 'adb_nice', _('Low Priority Service'), _('Reduce the priority of the adblock background processing to take fewer resources from the system. \ - Please note: This change requires a full adblock service restart to take effect.')); + o = s.taboption('additional', form.Flag, 'adb_nice', _('Low Priority Service'), _('Reduce the priority of the adblock background processing to take fewer resources from the system.')); o.enabled = '10'; o.rmempty = true; @@ -383,25 +286,19 @@ return view.extend({ o.datatype = 'range(1,300)'; o.rmempty = true; - o = s.taboption('additional', form.Value, 'adb_tmpbase', _('Base Temp Directory'), _('Base Temp Directory for all adblock related runtime operations, \ + o = s.taboption('additional', form.Value, 'adb_tmpbase', _('Base Temp Directory'), _('Base temp directory for all adblock related runtime operations, \ e.g. downloading, sorting, merging etc.')); o.placeholder = '/tmp'; o.rmempty = true; - o = s.taboption('additional', form.Flag, 'adb_backup', _('Blocklist Backup'), _('Create compressed blocklist backups, they will be used in case of download errors or during startup.')); - o.default = 1 - o.rmempty = false; - o = s.taboption('additional', form.Value, 'adb_backupdir', _('Backup Directory'), _('Target directory for blocklist backups.')); - o.depends('adb_backup', '1'); - o.placeholder = '/tmp/adblock-Backup'; + o.placeholder = '/tmp/adblock-backup'; o.rmempty = true; - o = s.taboption('additional', form.ListValue, 'adb_fetchutil', _('Download Utility'), _('List of supported and fully pre-configured download utilities.')); + o = s.taboption('additional', form.ListValue, 'adb_fetchcmd', _('Download Utility'), _('List of supported and fully pre-configured download utilities.')); o.value('uclient-fetch'); o.value('wget'); o.value('curl'); - o.value('aria2c'); o.optional = true; o.rmempty = true; @@ -409,71 +306,68 @@ return view.extend({ o.default = 0 o.rmempty = true; - o = s.taboption('additional', form.Value, 'adb_fetchparm', _('Download Parameters'), _('Manually override the pre-configured download options for the selected download utility.')); - o.optional = true; - o.rmempty = true; - /* advanced dns settings tab */ - o = s.taboption('adv_dns', form.ListValue, 'adb_dns', _('DNS Backend'), _('List of supported DNS backends with their default list directory. \ - To overwrite the default path use the \'DNS Directory\' option.')); - o.value('dnsmasq', _('dnsmasq (/tmp/dnsmasq.d)')); - o.value('unbound', _('unbound (/var/lib/unbound)')); - o.value('named', _('bind (/var/lib/bind)')); - o.value('smartdns', _('smartdns (/tmp/smartdns)')); - o.value('kresd', _('kresd (/etc/kresd)')); - o.value('raw', _('raw (/tmp)')); + o = s.taboption('adv_dns', form.DummyValue, '_sub'); + o.rawhtml = true; + o.default = '' + _('Changes on this tab needs an adblock service restart to take effect.') + '' + + '
'; + + o = s.taboption('adv_dns', form.ListValue, 'adb_dns', _('DNS Backend'), _('List of supported DNS backends.')); + o.value('dnsmasq', _('dnsmasq')); + o.value('unbound', _('unbound')); + o.value('named', _('bind')); + o.value('smartdns', _('smartdns')); + o.value('kresd', _('kresd')); + o.value('raw', _('raw')); o.optional = true; o.rmempty = true; - o = s.taboption('adv_dns', form.Value, 'adb_dnsdir', _('DNS Directory'), _('Target directory for the generated blocklist \'adb_list.overall\'.')); - o.placeholder = '/tmp'; + o = s.taboption('adv_dns', form.Flag, 'adb_dnsshift', _('Shift DNS Blocklist'), _('Shift the final DNS blocklist to the backup directory and only set a soft link to this file in memory. \ + As long as your backup directory resides on an external drive, enable this option to save memory.')); + o.rmempty = true; + + o = s.taboption('adv_dns', form.Flag, 'adb_dnsflush', _('Flush DNS Cache'), _('Empty the DNS cache before adblock processing starts to reduce the memory consumption.')); + o.rmempty = true; + + o = s.taboption('adv_dns', form.Value, 'adb_lookupdomain', _('DNS Lookup Domain'), _('Domain to check for a successful DNS backend restart.')); + o.placeholder = 'localhost'; + o.rmempty = true; + + o = s.taboption('adv_dns', form.Value, 'adb_dnsdir', _('DNS Directory'), _('Overwrite the default target directory for the generated blocklist.')); o.rmempty = true; o = s.taboption('adv_dns', form.ListValue, 'adb_dnsinstance', _('DNS Instance'), _('Set the dns backend instance used by adblock.')); + o.depends('adb_dns', 'dnsmasq'); o.value('0', _('First instance (default)')); o.value('1', _('Second instance')); o.value('2', _('Third instance')); - o.value('3', _('Fourth instance')); - o.value('4', _('Fifth instance')); - o.depends('adb_dns', 'dnsmasq'); o.optional = true; o.rmempty = true; o = s.taboption('adv_dns', form.Value, 'adb_dnstimeout', _('DNS Restart Timeout'), _('Timeout to wait for a successful DNS backend restart.')); o.placeholder = '20'; - o.datatype = 'range(1,60)'; - o.rmempty = true; - - o = s.taboption('adv_dns', form.Value, 'adb_lookupdomain', _('External DNS Lookup Domain'), _('External domain to check for a successful DNS backend restart. \ - Please note: To disable this check set this option to \'false\'.')); - o.placeholder = 'example.com'; + o.datatype = 'range(5,60)'; o.rmempty = true; - o = s.taboption('adv_dns', form.Flag, 'adb_dnsflush', _('Flush DNS Cache'), _('Empty the DNS cache before adblock processing starts to reduce the memory consumption.')); - o.rmempty = true; - - o = s.taboption('adv_dns', form.Flag, 'adb_dnsallow', _('Disable DNS Allow'), _('Disable selective DNS whitelisting (RPZ-PASSTHRU).')); - o.rmempty = true; - - o = s.taboption('adv_dns', form.DynamicList, 'adb_denyip', _('Block Local Client IPs'), _('Block all requests of certain DNS clients based on their IP address (RPZ-CLIENT-IP). \ - Please note: This feature is currently only supported by bind DNS backend.')); + o = s.taboption('adv_dns', form.DynamicList, 'adb_denyip', _('Block Local Client IPs'), _('Block all requests of certain DNS clients based on their IP address (RPZ-CLIENT-IP).')); o.datatype = 'or(ip4addr("nomask"),ip6addr("nomask"))'; + o.depends('adb_dns', 'bind'); o.optional = true; o.rmempty = true; - o = s.taboption('adv_dns', form.DynamicList, 'adb_allowip', _('Allow Local Client IPs'), _('Allow all requests of certain DNS clients based on their IP address (RPZ-CLIENT-IP). \ - Please note: This feature is currently only supported by bind DNS backend.')); + o = s.taboption('adv_dns', form.DynamicList, 'adb_allowip', _('Allow Local Client IPs'), _('Allow all requests of certain DNS clients based on their IP address (RPZ-CLIENT-IP).')); o.datatype = 'or(ip4addr("nomask"),ip6addr("nomask"))'; + o.depends('adb_dns', 'bind'); o.optional = true; o.rmempty = true; o = s.taboption('adv_dns', form.Flag, 'adb_jail', _('Additional Jail Blocklist'), _('Builds an additional DNS blocklist to block access to all domains except those listed in the whitelist. \ - Please note: You can use this restrictive blocklist e.g. for guest wifi or kidsafe configurations.')); + You can use this restrictive blocklist e.g. for guest wifi or kidsafe configurations.')); o.rmempty = true; - o = s.taboption('adv_dns', form.Value, 'adb_jaildir', _('Jail Directory'), _('Target directory for the generated jail blocklist \'adb_list.jail\'.')); + o = s.taboption('adv_dns', form.Value, 'adb_jaildir', _('Jail Directory'), _('Target directory for the generated jail blocklist.')); o.depends('adb_jail', '1'); o.placeholder = '/tmp'; o.rmempty = true; @@ -481,6 +375,11 @@ return view.extend({ /* advanced report settings tab */ + o = s.taboption('adv_report', form.DummyValue, '_sub'); + o.rawhtml = true; + o.default = '' + _('Changes on this tab needs an adblock service restart to take effect.') + '' + + '
'; + o = s.taboption('adv_report', form.DummyValue, '_sub'); o.rawhtml = true; o.default = 'Changes on this tab needs a full adblock service restart to take effect.'; @@ -490,7 +389,7 @@ return view.extend({ o.rmempty = true; o = s.taboption('adv_report', form.Value, 'adb_reportdir', _('Report Directory'), _('Target directory for DNS related report files.')); - o.placeholder = '/tmp/adblock-Report'; + o.placeholder = '/tmp/adblock-report'; o.rmempty = true; o = s.taboption('adv_report', form.Value, 'adb_repchunkcnt', _('Report Chunk Count'), _('Report chunk count used by tcpdump.')); @@ -513,6 +412,11 @@ return view.extend({ /* advanced email settings tab */ + o = s.taboption('adv_email', form.DummyValue, '_sub'); + o.rawhtml = true; + o.default = '' + _('Changes on this tab needs an adblock service restart to take effect.') + '' + + '
'; + o = s.taboption('adv_email', form.Value, 'adb_mailsender', _('E-Mail Sender Address'), _('Sender address for adblock notification E-Mails.')); o.placeholder = 'no-reply@adblock'; o.rmempty = true; @@ -526,47 +430,56 @@ return view.extend({ o.rmempty = true; /* - blocklist sources tab + feed selection tab */ - o = s.taboption('sources', form.DummyValue, '_sub'); - o.rawhtml = true; - o.default = 'List of supported and fully pre-configured adblock sources.
\ - List size information with the respective domain ranges as follows:
\ - • S (-10k), M (10k-30k) and L (30k-80k) should work for 128 MByte devices,
\ - • XL (80k-200k) should work for 256-512 MByte devices,
\ - • XXL (200k-) needs more RAM and Multicore support, e.g. x86 or raspberry devices.
\ - • VAR (50k-500k) variable size depending on the selection.
'; - - var name, size, focus, sources = []; - if (result[0]) { - sources = result[0].trim().split('\n'); + let feed, feeds, chain, descr; + if (result && Object.keys(result).length) { + if (result[0]) { + try { + feeds = JSON.parse(result[0]); + } catch (e) { + ui.addNotification(null, E('p', _('Unable to parse the custom feed file!')), 'error'); + } + } + if (result[1] && (!feeds || (feeds && !Object.keys(feeds).length))) { + try { + feeds = JSON.parse(result[1]); + } catch (e) { + ui.addNotification(null, E('p', _('Unable to parse the default feed file!')), 'error'); + } + } } - - o = s.taboption('sources', form.MultiValue, 'adb_sources', _('Sources (Size, Focus)')); - for (var i = 0; i < sources.length; i++) { - if (sources[i].match(/^\s+\+/)) { - name = sources[i].match(/^\s+\+\s(\w+)\s/)[1] || '-'; - size = sources[i].match(/^\s+\+\s\w+[\sx]+(\w+)/)[1] || '-'; - focus = sources[i].match(/^\s+\+\s\w+[\sx]+\w+\s+([\w\+]+)/)[1] || '-'; - o.value(name, name + ' (' + size + ', ' + focus + ')'); + o = s.taboption('feeds', form.DummyValue, '_sub'); + o.rawhtml = true; + o.default = '' + _('Changes on this tab needs an adblock service reload to take effect.') + '' + + '
' + + '' + _('External Blocklist Feeds') + ''; + + if (feeds && Object.keys(feeds).length) { + o = s.taboption('feeds', form.MultiValue, 'adb_feed', _('Blocklist Feed')); + for (let i = 0; i < Object.keys(feeds).length; i++) { + feed = Object.keys(feeds)[i].trim(); + chain = feeds[feed].size.trim() || 'in'; + descr = feeds[feed].descr.trim() || '-'; + o.value(feed, feed + ' (' + chain + ', ' + descr + ')'); } + o.optional = true; + o.rmempty = true; } - o.optional = true; - o.rmempty = true; /* prepare category data */ var code, category, list, path, categories = []; - if (result[1]) { - categories = result[1].trim().split('\n'); + if (result[2]) { + categories = result[2].trim().split('\n'); } - o = s.taboption('sources', form.DummyValue, '_sub'); + o = s.taboption('feeds', form.DummyValue, '_sub'); o.rawhtml = true; - o.default = 'UTCapitole Archive Selection'; + o.default = '
' + _('UTCapitole Archive Selection') + ''; - o = s.taboption('sources', form.DynamicList, 'adb_utc_sources', _('Categories')); + o = s.taboption('feeds', form.DynamicList, 'adb_utc_feed', _('Categories')); for (var i = 0; i < categories.length; i++) { code = categories[i].match(/^(\w+);/)[1].trim(); if (code === 'utc') { @@ -577,11 +490,11 @@ return view.extend({ o.optional = true; o.rmempty = true; - o = s.taboption('sources', form.DummyValue, '_sub'); + o = s.taboption('feeds', form.DummyValue, '_sub'); o.rawhtml = true; - o.default = 'StevenBlack List Selection'; + o.default = '
' + _('StevenBlack List Selection') + ''; - o = s.taboption('sources', form.DynamicList, 'adb_stb_sources', _('Variants')); + o = s.taboption('feeds', form.DynamicList, 'adb_stb_feed', _('Categories')); for (var i = 0; i < categories.length; i++) { code = categories[i].match(/^(\w+);/)[1].trim(); if (code === 'stb') { @@ -593,11 +506,11 @@ return view.extend({ o.optional = true; o.rmempty = true; - o = s.taboption('sources', form.DummyValue, '_sub'); + o = s.taboption('feeds', form.DummyValue, '_sub'); o.rawhtml = true; - o.default = 'Hagezi List Selection'; + o.default = '
' + _('Hagezi List Selection') + ''; - o = s.taboption('sources', form.DynamicList, 'adb_hag_sources', _('Variants')); + o = s.taboption('feeds', form.DynamicList, 'adb_hag_feed', _('Categories')); for (var i = 0; i < categories.length; i++) { code = categories[i].match(/^(\w+);/)[1].trim(); if (code === 'hag') { @@ -609,11 +522,11 @@ return view.extend({ o.optional = true; o.rmempty = true; - o = s.taboption('sources', form.DummyValue, '_sub'); + o = s.taboption('feeds', form.DummyValue, '_sub'); o.rawhtml = true; - o.default = '1Hosts List Selection'; + o.default = '
' + _('1Hosts List Selection') + ''; - o = s.taboption('sources', form.DynamicList, 'adb_hst_sources', _('Variants')); + o = s.taboption('feeds', form.DynamicList, 'adb_hst_feed', _('Categories')); for (var i = 0; i < categories.length; i++) { code = categories[i].match(/^(\w+);/)[1].trim(); if (code === 'hst') { @@ -625,7 +538,43 @@ return view.extend({ o.optional = true; o.rmempty = true; + s = m.section(form.NamedSection, 'global'); + s.render = L.bind(function () { + return E('div', { 'class': 'cbi-page-actions' }, [ + E('button', { + 'class': 'btn cbi-button cbi-button-negative important', + 'style': 'float:none;margin-right:.4em;', + 'click': ui.createHandlerFn(this, function () { + return handleAction('stop'); + }) + }, [_('Stop')]), + E('button', { + 'class': 'btn cbi-button cbi-button-apply important', + 'style': 'float:none;margin-right:.4em;', + 'id': 'btn_suspend', + 'click': ui.createHandlerFn(this, function () { + return handleAction('suspend'); + }) + }, [_('Suspend')]), + E('button', { + 'class': 'btn cbi-button cbi-button-positive important', + 'style': 'float:none;margin-right:.4em;', + 'click': ui.createHandlerFn(this, function () { + return handleAction('reload'); + }) + }, [_('Save & Reload')]), + E('button', { + 'class': 'btn cbi-button cbi-button-positive important', + 'style': 'float:none', + 'click': ui.createHandlerFn(this, function () { + handleAction('restart'); + }) + }, [_('Save & Restart')]) + ]) + }); return m.render(); }, + handleSaveApply: null, + handleSave: null, handleReset: null }); diff --git a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/whitelist.js b/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/whitelist.js deleted file mode 100644 index 9ffdbbd763..0000000000 --- a/applications/luci-app-adblock/htdocs/luci-static/resources/view/adblock/whitelist.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; -'require view'; -'require fs'; -'require ui'; - -return view.extend({ - load: function() { - return L.resolveDefault(fs.read_direct('/etc/adblock/adblock.whitelist'), ''); - }, - handleSave: function(ev) { - var value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n/g, '\n')) + '\n'; - return fs.write('/etc/adblock/adblock.whitelist', value) - .then(function(rc) { - document.querySelector('textarea').value = value; - ui.addNotification(null, E('p', _('The changes to the whitelist have been saved. Reload your adblock lists for the changes to take effect.')), 'info'); - }).catch(function(e) { - ui.addNotification(null, E('p', _('Unable to save changes: %s').format(e.message))); - }); - }, - render: function(whitelist) { - return E([ - E('p', {}, - _('This is the local adblock whitelist to always allow certain (sub) domains.
\ - Please note: add only one domain per line. Comments introduced with \'#\' are allowed - ip addresses, wildcards and regex are not.')), - E('p', {}, - E('textarea', { - 'style': 'width: 100% !important; padding: 5px; font-family: monospace', - 'spellcheck': 'false', - 'wrap': 'off', - 'rows': 25 - }, [ whitelist != null ? whitelist : '' ]) - ) - ]); - }, - handleSaveApply: null, - handleReset: null -}); diff --git a/applications/luci-app-adblock/root/usr/share/luci/menu.d/luci-app-adblock.json b/applications/luci-app-adblock/root/usr/share/luci/menu.d/luci-app-adblock.json index 9f2659c1da..807310b957 100644 --- a/applications/luci-app-adblock/root/usr/share/luci/menu.d/luci-app-adblock.json +++ b/applications/luci-app-adblock/root/usr/share/luci/menu.d/luci-app-adblock.json @@ -23,28 +23,36 @@ "path": "adblock/overview" } }, - "admin/services/adblock/dnsreport": { - "title": "DNS Report", + "admin/services/adblock/allowlist": { + "title": "Edit Allowlist", "order": 20, "action": { "type": "view", - "path": "adblock/dnsreport" + "path": "adblock/allowlist" } }, - "admin/services/adblock/blacklist": { - "title": "Edit Blacklist", + "admin/services/adblock/blocklist": { + "title": "Edit Blocklist", "order": 30, "action": { "type": "view", - "path": "adblock/blacklist" + "path": "adblock/blocklist" } }, - "admin/services/adblock/whitelist": { - "title": "Edit Whitelist", + "admin/services/adblock/feeds": { + "title": "Custom Feed Editor", "order": 40, "action": { "type": "view", - "path": "adblock/whitelist" + "path": "adblock/feeds" + } + }, + "admin/services/adblock/dnsreport": { + "title": "DNS Report", + "order": 50, + "action": { + "type": "view", + "path": "adblock/dnsreport" } }, "admin/services/adblock/logread": { diff --git a/applications/luci-app-adblock/root/usr/share/rpcd/acl.d/luci-app-adblock.json b/applications/luci-app-adblock/root/usr/share/rpcd/acl.d/luci-app-adblock.json index 61b6f3b57d..5fc914d3e7 100644 --- a/applications/luci-app-adblock/root/usr/share/rpcd/acl.d/luci-app-adblock.json +++ b/applications/luci-app-adblock/root/usr/share/rpcd/acl.d/luci-app-adblock.json @@ -2,33 +2,68 @@ "luci-app-adblock": { "description": "Grant access to LuCI app adblock", "write": { - "uci": ["adblock"], + "uci": [ + "adblock" + ], "file": { - "/etc/adblock/*": ["read"], - "/etc/adblock/adblock.blacklist": ["write"], - "/etc/adblock/adblock.whitelist": ["write"] + "/etc/adblock/*": [ + "read", + "write" + ], + "/etc/adblock/adblock.allowlist": [ + "write" + ], + "/etc/adblock/adblock.blocklist": [ + "write" + ], + "/etc/adblock/adblock.custom.feeds": [ + "read", + "write" + ] } }, "read": { - "cgi-io": [ "exec" ], + "cgi-io": [ + "exec" + ], "file": { - "/var/run/adblock.pid": ["read"], - "/tmp/adb_runtime.json": ["read"], - "/etc/crontabs/root": ["read"], - "/sbin/logread -e adblock-": [ "exec" ], - "/usr/sbin/logread -e adblock-": [ "exec" ], - "/etc/init.d/adblock list" : [ "exec" ], - "/etc/init.d/adblock reload" : [ "exec" ], - "/etc/init.d/adblock restart" : [ "exec" ], - "/etc/init.d/adblock suspend" : [ "exec" ], - "/etc/init.d/adblock resume" : [ "exec" ], - "/etc/init.d/adblock report [a-z]* [0-9]* [0-9]* *" : [ "exec" ], - "/etc/init.d/adblock timer list" : [ "exec" ], - "/etc/init.d/adblock timer remove [0-9]*" : [ "exec" ], - "/etc/init.d/adblock timer add * [0-9]* [0-9*]* [0-6,-*]*" : [ "exec" ], - "/etc/init.d/adblock query *" : [ "exec" ] + "/var/run/adblock.pid": [ + "read" + ], + "/var/run/adb_runtime.json": [ + "read" + ], + "/sbin/logread -e adblock-": [ + "exec" + ], + "/usr/sbin/logread -e adblock-": [ + "exec" + ], + "/etc/init.d/adblock reload": [ + "exec" + ], + "/etc/init.d/adblock restart": [ + "exec" + ], + "/etc/init.d/adblock suspend": [ + "exec" + ], + "/etc/init.d/adblock resume": [ + "exec" + ], + "/etc/init.d/adblock stop": [ + "exec" + ], + "/etc/init.d/adblock report [a-z]* [0-9]* [0-9]* *": [ + "exec" + ], + "/etc/init.d/adblock query *": [ + "exec" + ] }, - "uci": ["adblock"] + "uci": [ + "adblock" + ] } } -} +} \ No newline at end of file